package plogrus

import (
	"fmt"
	"io"
	"os"
	"path"
	"qingbing/xztools/crons"
	"qingbing/xzutils/files"
	"qingbing/xzutils/types"
	"time"

	"github.com/sirupsen/logrus"
)

/**
 * 定义数据格式化类型， 支持转化为 logrus.Formatter
 */
type Formatter func(*logrus.Entry) ([]byte, error)

// 实现 logrus.Formatter 接口
func (f Formatter) Format(entry *logrus.Entry) ([]byte, error) {
	return f(entry)
}

/**
 * 封装 logrus 结构体
 * 1. 实现数据格式化函数化
 * 2. 如果为文件类型， 支持按时间分割
 */
type Logrus struct {
	*logrus.Logger
	// 日志数据格式化
	Formatter Formatter
	// 日志文件切割时间间隔
	TimeInterval crons.TimeInterval
	LogDir       string
	Prefix       string
	fp           *os.File
	task         *crons.Cron
}

/**
 * 启动日志
 */
func (l *Logrus) Run() {
	if l.Formatter != nil {
		l.Logger.Formatter = l.Formatter
	}
	/**
	 * 确保 Writer 存在
	 */
	if l.Out == nil {
		/**
		 * 确保目录存在
		 */
		if l.LogDir == "" {
			panic(fmt.Errorf("日志未配置存放目录"))
		}
		if _, err := os.Stat(l.LogDir); os.IsNotExist(err) {
			err = os.MkdirAll(l.LogDir, 0777)
			if nil != err {
				panic(fmt.Errorf("创建日志存放目录失败(LogDir: %s; Error: %s)", l.LogDir, err))
			}
		}
		/**
		 * 维护日志写入句柄
		 */
		var discardWriter io.Writer
		l.task = crons.New()
		l.task.AddFunc(l.TimeInterval.String(), func() {
			filename := path.Join(l.LogDir, fmt.Sprintf("%s%s.log", l.Prefix, time.Now().Format(l.TimeInterval.DataFormat())))
			if fp, err := files.OpenFile(filename); err == nil {
				l.Out = fp
				if fp != nil {
					l.fp.Close()
				}
				l.fp = fp
			} else {
				if discardWriter == nil {
					discardWriter = &types.DiscardWriter{}
				}
				l.Out = discardWriter
			}
		}, true)
		l.task.Start()
	}
}

/**
 * 停用组件
 */
func (l *Logrus) Close() {
	if l.fp != nil {
		l.fp.Close()
	}
	if l.task != nil {
		l.task.Stop()
	}
}

/**
 * Logrus日志配置数据结构
 */
type Conf struct {
	*logrus.Logger
	// 日志数据格式化
	Formatter Formatter
	// 日志文件切割时间间隔
	TimeInterval crons.TimeInterval
	LogDir       string
	Prefix       string
}

/**
 * 新建 Logrus
 */
func New(conf *Conf) *Logrus {
	log := &Logrus{
		Logger:       conf.Logger,
		Formatter:    conf.Formatter,
		TimeInterval: conf.TimeInterval,
		LogDir:       conf.LogDir,
		Prefix:       conf.Prefix,
	}
	log.Run()
	return log
}
